/*
 ===========================================================================
 Copyright (C) 1999-2005 Id Software, Inc.
 Copyright (C) 2000-2006 Tim Angus

 This file is part of Tremulous.

 Tremulous is free software; you can redistribute it
 and/or modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation; either version 2 of the License,
 or (at your option) any later version.

 Tremulous is distributed in the hope that it will be
 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Tremulous; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ===========================================================================
 */

// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay


#include "cg_local.h"
#include "../ui/ui_shared.h"

// used for scoreboard
extern displayContextDef_t cgDC;
menuDef_t *menuScoreboard = NULL;

int drawTeamOverlayModificationCount = -1;

int sortedTeamPlayers[TEAM_MAXOVERLAY];
int numSortedTeamPlayers;
char systemChat[256];
char teamChat1[256];
char teamChat2[256];

//TA UI
int
CG_Text_Width(const char *text, float scale, int limit)
{
  int count, len;
  float out;
  glyphInfo_t *glyph;
  float useScale;
  // FIXME: see ui_main.c, same problem
  //  const unsigned char *s = text;
  const char *s = text;
  fontInfo_t *font = &cgDC.Assets.textFont;

  if (scale <= cg_smallFont.value)
    font = &cgDC.Assets.smallFont;
  else if (scale > cg_bigFont.value)
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  out = 0;

  if (text)
  {
    len = strlen(text);
    if (limit > 0 && len > limit)
      len = limit;

    count = 0;
    while(s && *s && count < len)
    {
      if (Q_IsColorString( s ))
      {
        s += 2;
        continue;
      }
      else
      {
        glyph = &font->glyphs[(int) *s];
        //TTimo: FIXME: getting nasty warnings without the cast,
        //hopefully this doesn't break the VM build
        out += glyph->xSkip;
        s++;
        count++;
      }
    }
  }

  return out * useScale;
}

int
CG_Text_Height(const char *text, float scale, int limit)
{
  int len, count;
  float max;
  glyphInfo_t *glyph;
  float useScale;
  // TTimo: FIXME
  //  const unsigned char *s = text;
  const char *s = text;
  fontInfo_t *font = &cgDC.Assets.textFont;

  if (scale <= cg_smallFont.value)
    font = &cgDC.Assets.smallFont;
  else if (scale > cg_bigFont.value)
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  max = 0;

  if (text)
  {
    len = strlen(text);
    if (limit > 0 && len > limit)
      len = limit;

    count = 0;
    while(s && *s && count < len)
    {
      if (Q_IsColorString( s ))
      {
        s += 2;
        continue;
      }
      else
      {
        glyph = &font->glyphs[(int) *s];
        //TTimo: FIXME: getting nasty warnings without the cast,
        //hopefully this doesn't break the VM build
        if (max < glyph->height)
          max = glyph->height;

        s++;
        count++;
      }
    }
  }

  return max * useScale;
}

void
CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t,
  float s2, float t2, qhandle_t hShader)
{
  float w, h;
  w = width * scale;
  h = height * scale;
  CG_AdjustFrom640(&x, &y, &w, &h);
  trap_R_DrawStretchPic(x, y, w, h, s, t, s2, t2, hShader);
}

void
CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust,
  int limit, int style)
{
  int len, count;
  vec4_t newColor;
  glyphInfo_t *glyph;
  float useScale;
  fontInfo_t *font = &cgDC.Assets.textFont;

  if (scale <= cg_smallFont.value)
    font = &cgDC.Assets.smallFont;
  else if (scale > cg_bigFont.value)
    font = &cgDC.Assets.bigFont;

  useScale = scale * font->glyphScale;
  if (text)
  {
    // TTimo: FIXME
    //    const unsigned char *s = text;
    const char *s = text;

    trap_R_SetColor(color);
    memcpy(&newColor[0], &color[0], sizeof(vec4_t));
    len = strlen(text);

    if (limit > 0 && len > limit)
      len = limit;

    count = 0;
    while(s && *s && count < len)
    {
      glyph = &font->glyphs[(int) *s];
      //TTimo: FIXME: getting nasty warnings without the cast,
      //hopefully this doesn't break the VM build

      if (Q_IsColorString( s ))
      {
        memcpy(newColor, g_color_table[ColorIndex( *( s + 1 ) )], sizeof(newColor));
        newColor[3] = color[3];
        trap_R_SetColor(newColor);
        s += 2;
        continue;
      }
      else
      {
        float yadj = useScale * glyph->top;
        if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE)
        {
          int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
          colorBlack[3] = newColor[3];
          trap_R_SetColor(colorBlack);
          CG_Text_PaintChar(
            x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s,
            glyph->t, glyph->s2, glyph->t2, glyph->glyph);

          colorBlack[3] = 1.0;
          trap_R_SetColor(newColor);
        }
        else if (style == ITEM_TEXTSTYLE_NEON)
        {
          vec4_t glow, outer, inner, white;

          glow[0] = newColor[0] * 0.5;
          glow[1] = newColor[1] * 0.5;
          glow[2] = newColor[2] * 0.5;
          glow[3] = newColor[3] * 0.2;

          outer[0] = newColor[0];
          outer[1] = newColor[1];
          outer[2] = newColor[2];
          outer[3] = newColor[3];

          inner[0] = newColor[0] * 1.5 > 1.0f ? 1.0f : newColor[0] * 1.5;
          inner[1] = newColor[1] * 1.5 > 1.0f ? 1.0f : newColor[1] * 1.5;
          inner[2] = newColor[2] * 1.5 > 1.0f ? 1.0f : newColor[2] * 1.5;
          inner[3] = newColor[3];

          white[0] = white[1] = white[2] = white[3] = 1.0f;

          trap_R_SetColor(glow);
          CG_Text_PaintChar(
            x - 3, y - yadj - 3, glyph->imageWidth + 6, glyph->imageHeight + 6, useScale, glyph->s,
            glyph->t, glyph->s2, glyph->t2, glyph->glyph);

          trap_R_SetColor(outer);
          CG_Text_PaintChar(
            x - 1, y - yadj - 1, glyph->imageWidth + 2, glyph->imageHeight + 2, useScale, glyph->s,
            glyph->t, glyph->s2, glyph->t2, glyph->glyph);

          trap_R_SetColor(inner);
          CG_Text_PaintChar(
            x - 0.5, y - yadj - 0.5, glyph->imageWidth + 1, glyph->imageHeight + 1, useScale,
            glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph);

          trap_R_SetColor(white);
        }

        CG_Text_PaintChar(
          x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t,
          glyph->s2, glyph->t2, glyph->glyph);

        x += (glyph->xSkip * useScale) + adjust;
        s++;
        count++;
      }
    }

    trap_R_SetColor(NULL);
  }
}

/*
 ==============
 CG_DrawFieldPadded

 Draws large numbers for status bar and powerups
 ==============
 */
static void
CG_DrawFieldPadded(int x, int y, int width, int cw, int ch, int value)
{
  char num[16], *ptr;
  int l, orgL;
  int frame;
  int charWidth, charHeight;

  if (!(charWidth = cw))
    charWidth = CHAR_WIDTH;

  if (!(charHeight = ch))
    charWidth = CHAR_HEIGHT;

  if (width < 1)
    return;

  // draw number string
  if (width > 4)
    width = 4;

  switch(width)
  {
    case 1:
      value = value > 9 ? 9 : value;
      value = value < 0 ? 0 : value;
      break;
    case 2:
      value = value > 99 ? 99 : value;
      value = value < -9 ? -9 : value;
      break;
    case 3:
      value = value > 999 ? 999 : value;
      value = value < -99 ? -99 : value;
      break;
    case 4:
      value = value > 9999 ? 9999 : value;
      value = value < -999 ? -999 : value;
      break;
  }

  Com_sprintf(num, sizeof(num), "%d", value);
  l = strlen(num);

  if (l > width)
    l = width;

  orgL = l;

  x += 2;

  ptr = num;
  while(*ptr && l)
  {
    if (width > orgL)
    {
      CG_DrawPic(x, y, charWidth, charHeight, cgs.media.numberShaders[0]);
      width--;
      x += charWidth;
      continue;
    }

    if (*ptr == '-')
      frame = STAT_MINUS;
    else
      frame = *ptr - '0';

    CG_DrawPic(x, y, charWidth, charHeight, cgs.media.numberShaders[frame]);
    x += charWidth;
    ptr++;
    l--;
  }
}

/*
 ==============
 CG_DrawField

 Draws large numbers for status bar and powerups
 ==============
 */
static void
CG_DrawField(int x, int y, int width, int cw, int ch, int value)
{
  char num[16], *ptr;
  int l;
  int frame;
  int charWidth, charHeight;

  if (!(charWidth = cw))
    charWidth = CHAR_WIDTH;

  if (!(charHeight = ch))
    charWidth = CHAR_HEIGHT;

  if (width < 1)
    return;

  // draw number string
  if (width > 4)
    width = 4;

  switch(width)
  {
    case 1:
      value = value > 9 ? 9 : value;
      value = value < 0 ? 0 : value;
      break;
    case 2:
      value = value > 99 ? 99 : value;
      value = value < -9 ? -9 : value;
      break;
    case 3:
      value = value > 999 ? 999 : value;
      value = value < -99 ? -99 : value;
      break;
    case 4:
      value = value > 9999 ? 9999 : value;
      value = value < -999 ? -999 : value;
      break;
  }

  Com_sprintf(num, sizeof(num), "%d", value);
  l = strlen(num);

  if (l > width)
    l = width;

  x += 2 + charWidth * (width - l);

  ptr = num;
  while(*ptr && l)
  {
    if (*ptr == '-')
      frame = STAT_MINUS;
    else
      frame = *ptr - '0';

    CG_DrawPic(x, y, charWidth, charHeight, cgs.media.numberShaders[frame]);
    x += charWidth;
    ptr++;
    l--;
  }
}

static void
CG_DrawProgressBar(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special, float progress)
{
  float rimWidth = rect->h / 20.0f;
  float doneWidth, leftWidth;
  float tx, ty, tw, th;
  char textBuffer[8];

  if (rimWidth < 0.6f)
    rimWidth = 0.6f;

  if (special >= 0.0f)
    rimWidth = special;

  if (progress < 0.0f)
    progress = 0.0f;
  else if (progress > 1.0f)
    progress = 1.0f;

  doneWidth = (rect->w - 2 * rimWidth) * progress;
  leftWidth = (rect->w - 2 * rimWidth) - doneWidth;

  trap_R_SetColor(color);

  //draw rim and bar
  if (align == ITEM_ALIGN_RIGHT)
  {
    CG_DrawPic(rect->x, rect->y, rimWidth, rect->h, cgs.media.whiteShader);
    CG_DrawPic(rect->x + rimWidth, rect->y, leftWidth, rimWidth, cgs.media.whiteShader);
    CG_DrawPic(
      rect->x + rimWidth, rect->y + rect->h - rimWidth, leftWidth, rimWidth, cgs.media.whiteShader);
    CG_DrawPic(
      rect->x + rimWidth + leftWidth, rect->y, rimWidth + doneWidth, rect->h, cgs.media.whiteShader);
  }
  else
  {
    CG_DrawPic(rect->x, rect->y, rimWidth + doneWidth, rect->h, cgs.media.whiteShader);
    CG_DrawPic(rimWidth + rect->x + doneWidth, rect->y, leftWidth, rimWidth, cgs.media.whiteShader);
    CG_DrawPic(
      rimWidth + rect->x + doneWidth, rect->y + rect->h - rimWidth, leftWidth, rimWidth,
      cgs.media.whiteShader);
    CG_DrawPic(rect->x + rect->w - rimWidth, rect->y, rimWidth, rect->h, cgs.media.whiteShader);
  }

  trap_R_SetColor(NULL);

  //draw text
  if (scale > 0.0)
  {
    Com_sprintf(textBuffer, sizeof(textBuffer), "%d%%", (int) (progress * 100));
    tw = CG_Text_Width(textBuffer, scale, 0);
    th = scale * 40.0f;

    switch(align)
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x + (rect->w / 10.0f);
        ty = rect->y + (rect->h / 2.0f) + (th / 2.0f);
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - (rect->w / 10.0f) - tw;
        ty = rect->y + (rect->h / 2.0f) + (th / 2.0f);
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + (rect->w / 2.0f) - (tw / 2.0f);
        ty = rect->y + (rect->h / 2.0f) + (th / 2.0f);
        break;

      default:
        tx = ty = 0.0f;
    }

    CG_Text_Paint(tx, ty, scale, color, textBuffer, 0, 0, textStyle);
  }
}

//=============== TA: was cg_newdraw.c

void
CG_InitTeamChat(void)
{
  memset(teamChat1, 0, sizeof(teamChat1));
  memset(teamChat2, 0, sizeof(teamChat2));
  memset(systemChat, 0, sizeof(systemChat));
}

void
CG_SetPrintString(int type, const char *p)
{
  if (type == SYSTEM_PRINT)
  {
    strcpy(systemChat, p);
  }
  else
  {
    strcpy(teamChat2, teamChat1);
    strcpy(teamChat1, p);
  }
}

#define NO_CREDITS_TIME 2000

static void
CG_DrawPlayerCreditsValue(rectDef_t *rect, vec4_t color, qboolean padding)
{
  int value;
  playerState_t *ps;
  centity_t *cent;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  //if the build timer pie is showing don't show this
  if ((cent->currentState.weapon == WP_ABUILD || cent->currentState.weapon == WP_ABUILD2)
      && ps->stats[STAT_MISC])
    return;

  value = ps->persistant[PERS_CREDIT];
  if (value > -1)
  {
    if (cg.predictedPlayerState.stats[STAT_PTEAM] == PTE_ALIENS && !CG_AtHighestClass())
    {
      if (cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME)
      {
        if (((cg.time - cg.lastEvolveAttempt) / 300) % 2)
          color[3] = 0.0f;
      }
    }

    trap_R_SetColor(color);

    if (padding)
      CG_DrawFieldPadded(rect->x, rect->y, 4, rect->w / 4, rect->h, value);
    else
      CG_DrawField(rect->x, rect->y, 1, rect->w, rect->h, value);

    trap_R_SetColor(NULL);
  }
}

static void
CG_DrawPlayerBankValue(rectDef_t *rect, vec4_t color, qboolean padding)
{
  int value;
  playerState_t *ps;

  ps = &cg.snap->ps;

  value = ps->persistant[PERS_BANK];
  if (value > -1)
  {
    trap_R_SetColor(color);

    if (padding)
      CG_DrawFieldPadded(rect->x, rect->y, 4, rect->w / 4, rect->h, value);
    else
      CG_DrawField(rect->x, rect->y, 1, rect->w, rect->h, value);

    trap_R_SetColor(NULL);
  }
}

#define HH_MIN_ALPHA  0.2f
#define HH_MAX_ALPHA  0.8f
#define HH_ALPHA_DIFF (HH_MAX_ALPHA-HH_MIN_ALPHA)

#define AH_MIN_ALPHA  0.2f
#define AH_MAX_ALPHA  0.8f
#define AH_ALPHA_DIFF (AH_MAX_ALPHA-AH_MIN_ALPHA)

/*
 ==============
 CG_DrawPlayerStamina1
 ==============
 */
static void
CG_DrawPlayerStamina1(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  float stamina = ps->stats[STAT_STAMINA];
  float maxStaminaBy3 = (float) MAX_STAMINA / 3.0f;
  float progress;

  stamina -= (2 * (int) maxStaminaBy3);
  progress = stamina / maxStaminaBy3;

  if (progress > 1.0f)
    progress = 1.0f;
  else if (progress < 0.0f)
    progress = 0.0f;

  color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerStamina2
 ==============
 */
static void
CG_DrawPlayerStamina2(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  float stamina = ps->stats[STAT_STAMINA];
  float maxStaminaBy3 = (float) MAX_STAMINA / 3.0f;
  float progress;

  stamina -= (int) maxStaminaBy3;
  progress = stamina / maxStaminaBy3;

  if (progress > 1.0f)
    progress = 1.0f;
  else if (progress < 0.0f)
    progress = 0.0f;

  color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerStamina3
 ==============
 */
static void
CG_DrawPlayerStamina3(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  float stamina = ps->stats[STAT_STAMINA];
  float maxStaminaBy3 = (float) MAX_STAMINA / 3.0f;
  float progress;

  progress = stamina / maxStaminaBy3;

  if (progress > 1.0f)
    progress = 1.0f;
  else if (progress < 0.0f)
    progress = 0.0f;

  color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerStamina4
 ==============
 */
static void
CG_DrawPlayerStamina4(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  float stamina = ps->stats[STAT_STAMINA];
  float progress;

  stamina += (float) MAX_STAMINA;
  progress = stamina / (float) MAX_STAMINA;

  if (progress > 1.0f)
    progress = 1.0f;
  else if (progress < 0.0f)
    progress = 0.0f;

  color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerStaminaBolt
 ==============
 */
static void
CG_DrawPlayerStaminaBolt(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  float stamina = ps->stats[STAT_STAMINA];

  if (stamina < 0)
    color[3] = HH_MIN_ALPHA;
  else
    color[3] = HH_MAX_ALPHA;

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerClipsRing
 ==============
 */
static void
CG_DrawPlayerClipsRing(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  centity_t *cent;
  float buildTime = ps->stats[STAT_MISC];
  float progress;
  float maxDelay;

  cent = &cg_entities[cg.snap->ps.clientNum];

  switch(cent->currentState.weapon)
  {
    case WP_ABUILD:
    case WP_ABUILD2:
    case WP_HBUILD:
    case WP_HBUILD2:
      maxDelay = (float) BG_FindBuildDelayForWeapon(cent->currentState.weapon);

      if (buildTime > maxDelay)
        buildTime = maxDelay;

      progress = (maxDelay - buildTime) / maxDelay;

      color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);
      break;

    default:
      if (ps->weaponstate == WEAPON_RELOADING)
      {
        maxDelay = (float) BG_FindReloadTimeForWeapon(cent->currentState.weapon);
        progress = (maxDelay - (float) ps->weaponTime) / maxDelay;

        color[3] = HH_MIN_ALPHA + (progress * HH_ALPHA_DIFF);
      }
      break;
  }

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerBuildTimerRing
 ==============
 */
static void
CG_DrawPlayerBuildTimerRing(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  centity_t *cent;
  float buildTime = ps->stats[STAT_MISC];
  float progress;
  float maxDelay;

  cent = &cg_entities[cg.snap->ps.clientNum];

  maxDelay = (float) BG_FindBuildDelayForWeapon(cent->currentState.weapon);

  if (buildTime > maxDelay)
    buildTime = maxDelay;

  progress = (maxDelay - buildTime) / maxDelay;

  color[3] = AH_MIN_ALPHA + (progress * AH_ALPHA_DIFF);

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerBoosted
 ==============
 */
static void
CG_DrawPlayerBoosted(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  qboolean boosted = ps->stats[STAT_STATE] & SS_BOOSTED;

  if (boosted)
    color[3] = AH_MAX_ALPHA;
  else
    color[3] = AH_MIN_ALPHA;

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerBoosterBolt
 ==============
 */
static void
CG_DrawPlayerBoosterBolt(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  qboolean boosted = ps->stats[STAT_STATE] & SS_BOOSTED;
  vec4_t localColor;

  Vector4Copy( color, localColor );

  if (boosted)
  {
    if (ps->stats[STAT_BOOSTTIME] > BOOST_TIME - 3000)
    {
      qboolean flash = (ps->stats[STAT_BOOSTTIME] / 500) % 2;

      if (flash)
        localColor[3] = 1.0f;
    }
  }

  trap_R_SetColor(localColor);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerPoisonBarbs
 ==============
 */
static void
CG_DrawPlayerPoisonBarbs(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  int x = rect->x;
  int y = rect->y;
  int width = rect->w;
  int height = rect->h;
  qboolean vertical;
  int iconsize, numBarbs, i;

  BG_UnpackAmmoArray(ps->weapon, &ps->ammo, ps->powerups, &numBarbs, NULL);

  if (height > width)
  {
    vertical = qtrue;
    iconsize = width;
  }
  else if (height <= width)
  {
    vertical = qfalse;
    iconsize = height;
  }

  if (color[3] != 0.0)
    trap_R_SetColor(color);

  for(i = 0;i < numBarbs;i++)
  {
    if (vertical)
      y += iconsize;
    else
      x += iconsize;

    CG_DrawPic(x, y, iconsize, iconsize, shader);
  }

  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawPlayerWallclimbing
 ==============
 */
static void
CG_DrawPlayerWallclimbing(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  qboolean ww = ps->stats[STAT_STATE] & SS_WALLCLIMBING;

  if (ww)
    color[3] = AH_MAX_ALPHA;
  else
    color[3] = AH_MIN_ALPHA;

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

static void
CG_DrawPlayerStamina(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  playerState_t *ps = &cg.snap->ps;
  int stamina = ps->stats[STAT_STAMINA];
  float progress = ((float) stamina + (float) MAX_STAMINA) / ((float) MAX_STAMINA * 2.0f);

  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, progress);
}

static void
CG_DrawPlayerAmmoValue(rectDef_t *rect, vec4_t color)
{
  int value;
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  if (cent->currentState.weapon)
  {
    switch(cent->currentState.weapon)
    {
      case WP_ABUILD:
      case WP_ABUILD2:
        //percentage of BP remaining
        value = cgs.alienBuildPoints;
        break;

      case WP_HBUILD:
      case WP_HBUILD2:
        //percentage of BP remaining
        if (cg.snap->ps.stats[STAT_PTEAM] == PTE_HUMANS)
        {
          value = cgs.humanBuildPoints;
        }
        else
        {
          value = cgs.alienBuildPoints;
        }
        break;

      default:
        BG_UnpackAmmoArray(cent->currentState.weapon, &ps->ammo, ps->powerups, &value, NULL);
        break;
    }

    if (value > 999)
      value = 999;

    if (value > -1)
    {
      trap_R_SetColor(color);
      CG_DrawField(rect->x, rect->y, 4, rect->w / 4, rect->h, value);
      trap_R_SetColor(NULL);
    }
  }
}

/*
 ==============
 CG_DrawAlienSense
 ==============
 */
static void
CG_DrawAlienSense(rectDef_t *rect)
{
  if (BG_ClassHasAbility(cg.snap->ps.stats[STAT_PCLASS], SCA_ALIENSENSE))
    CG_AlienSense(rect);
}

/*
 ==============
 CG_DrawHumanScanner
 ==============
 */
static void
CG_DrawHumanScanner(rectDef_t *rect, qhandle_t shader, vec4_t color)
{
  //if( BG_InventoryContainsUpgrade( UP_HELMET, cg.snap->ps.stats ) )
  //CG_Scanner( rect, shader, color );
}

/*
 ==============
 CG_DrawUsableBuildable
 ==============
 */
static void
CG_DrawUsableBuildable(rectDef_t *rect, qhandle_t shader, vec4_t color)
{
  vec3_t view, point;
  trace_t trace;
  entityState_t *es;

  AngleVectors(cg.refdefViewAngles, view, NULL, NULL);
  VectorMA( cg.refdef.vieworg, 64, view, point );
  CG_Trace(
    &trace, cg.refdef.vieworg, NULL, NULL, point, cg.predictedPlayerState.clientNum, MASK_SHOT);

  es = &cg_entities[trace.entityNum].currentState;

  if (es->eType == ET_BUILDABLE && BG_FindUsableForBuildable(es->modelindex)
      && cg.predictedPlayerState.stats[STAT_PTEAM] == BG_FindTeamForBuildable(es->modelindex))
  {
    //hack to prevent showing the usable buildable when you aren't carrying an energy weapon
    if ((es->modelindex == BA_H_REACTOR || es->modelindex == BA_H_REPEATER)
        && (!BG_FindUsesEnergyForWeapon(cg.snap->ps.weapon) || BG_FindInfinteAmmoForWeapon(
          cg.snap->ps.weapon)))
      return;

    trap_R_SetColor(color);
    CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
    trap_R_SetColor(NULL);
  }
}

#define BUILD_DELAY_TIME  2000

static void
CG_DrawPlayerBuildTimer(rectDef_t *rect, vec4_t color)
{
  float progress;
  int index;
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  if (cent->currentState.weapon)
  {
    switch(cent->currentState.weapon)
    {
      case WP_ABUILD:
        progress = (float) ps->stats[STAT_MISC] / (float) ABUILDER_BASE_DELAY;
        break;

      case WP_ABUILD2:
        progress = (float) ps->stats[STAT_MISC] / (float) ABUILDER_ADV_DELAY;
        break;

      case WP_HBUILD:
        progress = (float) ps->stats[STAT_MISC] / (float) HBUILD_DELAY;
        break;

      case WP_HBUILD2:
        progress = (float) ps->stats[STAT_MISC] / (float) HBUILD2_DELAY;
        break;

      default:
        return;
        break;
    }

    if (!ps->stats[STAT_MISC])
      return;

    index = (int) (progress * 8.0f);

    if (index > 7)
      index = 7;
    else if (index < 0)
      index = 0;

    if (cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME)
    {
      if (((cg.time - cg.lastBuildAttempt) / 300) % 2)
      {
        color[0] = 1.0f;
        color[1] = color[2] = 0.0f;
        color[3] = 1.0f;
      }
    }

    trap_R_SetColor(color);
    CG_DrawPic(rect->x, rect->y, rect->w, rect->h, cgs.media.buildWeaponTimerPie[index]);
    trap_R_SetColor(NULL);
  }
}

static void
CG_DrawPlayerClipsValue(rectDef_t *rect, vec4_t color)
{
  int value;
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  if (cent->currentState.weapon)
  {
    switch(cent->currentState.weapon)
    {
      case WP_ABUILD:
      case WP_ABUILD2:
      case WP_HBUILD:
      case WP_HBUILD2:
        break;

      default:
        BG_UnpackAmmoArray(cent->currentState.weapon, &ps->ammo, ps->powerups, NULL, &value);

        if (value > -1)
        {
          trap_R_SetColor(color);
          CG_DrawField(rect->x, rect->y, 4, rect->w / 4, rect->h, value);
          trap_R_SetColor(NULL);
        }
        break;
    }
  }
}

static void
CG_DrawPlayerHealthValue(rectDef_t *rect, vec4_t color)
{
  playerState_t *ps;
  int value;

  ps = &cg.snap->ps;

  value = ps->stats[STAT_HEALTH];

  trap_R_SetColor(color);
  CG_DrawField(rect->x, rect->y, 4, rect->w / 4, rect->h, value);
  trap_R_SetColor(NULL);
}

static void
CG_DrawPlayerHealthBar(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  playerState_t *ps;
  float total;

  ps = &cg.snap->ps;

  total = ((float) ps->stats[STAT_HEALTH] / (float) ps->stats[STAT_MAX_HEALTH]);
  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, total);
}

/*
 ==============
 CG_DrawPlayerHealthCross
 ==============
 */
static void
CG_DrawPlayerHealthCross(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps = &cg.snap->ps;
  int health = ps->stats[STAT_HEALTH];

  if (health < 10)
  {
    color[0] = 1.0f;
    color[1] = color[2] = 0.0f;
  }

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

static void
CG_DrawProgressLabel(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale,
  int align, const char *s, float fraction)
{
  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };
  float tx, tw = CG_Text_Width(s, scale, 0);

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = 0.0f;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->w - tw;
      break;

    case ITEM_ALIGN_CENTER:
      tx = (rect->w / 2.0f) - (tw / 2.0f);
      break;

    default:
      tx = 0.0f;
  }

  if (fraction < 1.0f)
    CG_Text_Paint(
      rect->x + text_x + tx, rect->y + text_y, scale, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL);
  else
    CG_Text_Paint(
      rect->x + text_x + tx, rect->y + text_y, scale, color, s, 0, 0, ITEM_TEXTSTYLE_NEON);
}

static void
CG_DrawMediaProgress(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, cg.mediaFraction);
}

static void
CG_DrawMediaProgressLabel(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale,
  int align)
{
  CG_DrawProgressLabel(
    rect, text_x, text_y, color, scale, align, "Map and Textures", cg.mediaFraction);
}

static void
CG_DrawBuildablesProgress(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, cg.buildablesFraction);
}

static void
CG_DrawBuildablesProgressLabel(rectDef_t *rect, float text_x, float text_y, vec4_t color,
  float scale, int align)
{
  CG_DrawProgressLabel(
    rect, text_x, text_y, color, scale, align, "Buildable Models", cg.buildablesFraction);
}

static void
CG_DrawCharModelProgress(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, cg.charModelFraction);
}

static void
CG_DrawCharModelProgressLabel(rectDef_t *rect, float text_x, float text_y, vec4_t color,
  float scale, int align)
{
  CG_DrawProgressLabel(
    rect, text_x, text_y, color, scale, align, "Character Models", cg.charModelFraction);
}

static void
CG_DrawOverallProgress(rectDef_t *rect, vec4_t color, float scale, int align, int textStyle,
  int special)
{
  float total;

  total = (cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction) / 3.0f;
  CG_DrawProgressBar(rect, color, scale, align, textStyle, special, total);
}

static void
CG_DrawLevelShot(rectDef_t *rect)
{
  const char *s;
  const char *info;
  qhandle_t levelshot;
  qhandle_t detail;

  info = CG_ConfigString(CS_SERVERINFO);
  s = Info_ValueForKey(info, "mapname");
  levelshot = trap_R_RegisterShaderNoMip(va("levelshots/%s.tga", s));

  if (!levelshot)
    levelshot = trap_R_RegisterShaderNoMip("gfx/2d/load_screen");

  trap_R_SetColor(NULL);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, levelshot);

  // blend a detail texture over it
  detail = trap_R_RegisterShader("gfx/misc/detail");
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, detail);
}

static void
CG_DrawLoadingString(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale,
  int align, int textStyle, const char *s)
{
  float tw, th, tx;
  int pos, i;
  char buffer[1024];
  char *end;

  if (!s[0])
    return;

  strcpy(buffer, s);
  tw = CG_Text_Width(s, scale, 0);
  th = scale * 40.0f;

  pos = i = 0;

  while(pos < strlen(s))
  {
    strcpy(buffer, &s[pos]);
    tw = CG_Text_Width(buffer, scale, 0);

    while(tw > rect->w)
    {
      end = strrchr(buffer, ' ');

      if (end == NULL)
        break;

      *end = '\0';
      tw = CG_Text_Width(buffer, scale, 0);
    }

    switch(align)
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x;
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - tw;
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + (rect->w / 2.0f) - (tw / 2.0f);
        break;

      default:
        tx = 0.0f;
    }

    CG_Text_Paint(
      tx + text_x, rect->y + text_y + i * (th + 3), scale, color, buffer, 0, 0, textStyle);

    pos += strlen(buffer) + 1;
    i++;
  }
}

static void
CG_DrawLevelName(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle)
{
  const char *s;

  s = CG_ConfigString(CS_MESSAGE);

  CG_DrawLoadingString(rect, text_x, text_y, color, scale, align, textStyle, s);
}

static void
CG_DrawMOTD(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle)
{
  const char *s;

  s = CG_ConfigString(CS_MOTD);

  CG_DrawLoadingString(rect, text_x, text_y, color, scale, align, textStyle, s);
}

static void
CG_DrawHostname(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle)
{
  char buffer[1024];
  const char *info;

  info = CG_ConfigString(CS_SERVERINFO);

  Q_strncpyz(buffer, Info_ValueForKey(info, "sv_hostname"), 1024);
  Q_CleanStr(buffer);

  CG_DrawLoadingString(rect, text_x, text_y, color, scale, align, textStyle, buffer);
}

/*
 ==============
 CG_DrawDemoPlayback
 ==============
 */
static void
CG_DrawDemoPlayback(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  if (!cg_drawDemoState.integer)
    return;

  if (trap_GetDemoState() != DS_PLAYBACK)
    return;

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ==============
 CG_DrawDemoRecording
 ==============
 */
static void
CG_DrawDemoRecording(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  if (!cg_drawDemoState.integer)
    return;

  if (trap_GetDemoState() != DS_RECORDING)
    return;

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

/*
 ======================
 CG_UpdateMediaFraction

 ======================
 */
void
CG_UpdateMediaFraction(float newFract)
{
  cg.mediaFraction = newFract;

  trap_UpdateScreen();
}

/*
 ====================
 CG_DrawLoadingScreen

 Draw all the status / pacifier stuff during level loading
 ====================
 */
void
CG_DrawLoadingScreen(void)
{
  Menu_Paint(Menus_FindByName("Loading"), qtrue);
}

float
CG_GetValue(int ownerDraw)
{
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  switch(ownerDraw)
  {
    case CG_PLAYER_AMMO_VALUE:
      if (cent->currentState.weapon)
      {
        int value;

        BG_UnpackAmmoArray(cent->currentState.weapon, &ps->ammo, ps->powerups, &value, NULL);

        return value;
      }
      break;
    case CG_PLAYER_CLIPS_VALUE:
      if (cent->currentState.weapon)
      {
        int value;

        BG_UnpackAmmoArray(cent->currentState.weapon, &ps->ammo, ps->powerups, NULL, &value);

        return value;
      }
      break;
    case CG_PLAYER_HEALTH:
      return ps->stats[STAT_HEALTH];
      break;
    default:
      break;
  }

  return -1;
}

static void
CG_DrawAreaSystemChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader)
{
  CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, systemChat, 0, 0, 0);
}

static void
CG_DrawAreaTeamChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader)
{
  CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, teamChat1, 0, 0, 0);
}

static void
CG_DrawAreaChat(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader)
{
  CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, teamChat2, 0, 0, 0);
}

const char *
CG_GetKillerText()
{
  const char *s = "";
  if (cg.killerName[0])
    s = va("Fragged by %s", cg.killerName);

  return s;
}

static void
CG_DrawKiller(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle)
{
  // fragged by ... line
  if (cg.killerName[0])
  {
    int x = rect->x + rect->w / 2;
    CG_Text_Paint(
      x - CG_Text_Width(CG_GetKillerText(), scale, 0) / 2, rect->y + rect->h, scale, color,
      CG_GetKillerText(), 0, 0, textStyle);
  }
}

static void
CG_Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text,
  float adjust, int limit)
{
  int len, count;
  vec4_t newColor;
  glyphInfo_t *glyph;

  if (text)
  {
    // TTimo: FIXME
    //    const unsigned char *s = text; // bk001206 - unsigned
    const char *s = text;
    float max = *maxX;
    float useScale;
    fontInfo_t *font = &cgDC.Assets.textFont;

    if (scale <= cg_smallFont.value)
      font = &cgDC.Assets.smallFont;
    else if (scale > cg_bigFont.value)
      font = &cgDC.Assets.bigFont;

    useScale = scale * font->glyphScale;
    trap_R_SetColor(color);
    len = strlen(text);

    if (limit > 0 && len > limit)
      len = limit;

    count = 0;

    while(s && *s && count < len)
    {
      glyph = &font->glyphs[(int) *s];
      //TTimo: FIXME: getting nasty warnings without the cast,
      //hopefully this doesn't break the VM build

      if (Q_IsColorString( s ))
      {
        memcpy(newColor, g_color_table[ColorIndex( *(s+1) )], sizeof(newColor));
        newColor[3] = color[3];
        trap_R_SetColor(newColor);
        s += 2;
        continue;
      }
      else
      {
        float yadj = useScale * glyph->top;

        if (CG_Text_Width(s, useScale, 1) + x > max)
        {
          *maxX = 0;
          break;
        }

        CG_Text_PaintChar(
          x, y - yadj, glyph->imageWidth, glyph->imageHeight, useScale, glyph->s, glyph->t,
          glyph->s2, glyph->t2, glyph->glyph);
        x += (glyph->xSkip * useScale) + adjust;
        *maxX = x;
        count++;
        s++;
      }
    }

    trap_R_SetColor(NULL);
  }
}

static void
CG_DrawTeamSpectators(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader)
{
  if (cg.spectatorLen)
  {
    float maxX;

    if (cg.spectatorWidth == -1)
    {
      cg.spectatorWidth = 0;
      cg.spectatorPaintX = rect->x + 1;
      cg.spectatorPaintX2 = -1;
    }

    if (cg.spectatorOffset > cg.spectatorLen)
    {
      cg.spectatorOffset = 0;
      cg.spectatorPaintX = rect->x + 1;
      cg.spectatorPaintX2 = -1;
    }

    if (cg.time > cg.spectatorTime)
    {
      cg.spectatorTime = cg.time + 10;

      if (cg.spectatorPaintX <= rect->x + 2)
      {
        if (cg.spectatorOffset < cg.spectatorLen)
        {
          //TA: skip colour directives
          if (Q_IsColorString( &cg.spectatorList[ cg.spectatorOffset ] ))
            cg.spectatorOffset += 2;
          else
          {
            cg.spectatorPaintX += CG_Text_Width(&cg.spectatorList[cg.spectatorOffset], scale, 1)
                - 1;
            cg.spectatorOffset++;
          }
        }
        else
        {
          cg.spectatorOffset = 0;

          if (cg.spectatorPaintX2 >= 0)
            cg.spectatorPaintX = cg.spectatorPaintX2;
          else
            cg.spectatorPaintX = rect->x + rect->w - 2;

          cg.spectatorPaintX2 = -1;
        }
      }
      else
      {
        cg.spectatorPaintX--;

        if (cg.spectatorPaintX2 >= 0)
          cg.spectatorPaintX2--;
      }
    }

    maxX = rect->x + rect->w - 2;

    CG_Text_Paint_Limit(
      &maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color,
      &cg.spectatorList[cg.spectatorOffset], 0, 0);

    if (cg.spectatorPaintX2 >= 0)
    {
      float maxX2 = rect->x + rect->w - 2;
      CG_Text_Paint_Limit(
        &maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale, color, cg.spectatorList, 0,
        cg.spectatorOffset);
    }

    if (cg.spectatorOffset && maxX > 0)
    {
      // if we have an offset ( we are skipping the first part of the string ) and we fit the string
      if (cg.spectatorPaintX2 == -1)
        cg.spectatorPaintX2 = rect->x + rect->w - 2;
    }
    else
      cg.spectatorPaintX2 = -1;
  }
}

/*
 ==================
 CG_DrawStageReport
 ==================
 */
static void
CG_DrawStageReport(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale,
  int align, int textStyle)
{
  char s[MAX_TOKEN_CHARS];
  int tx, w, kills;

  if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && !cg.intermissionStarted)
    return;

  //  if (cg.intermissionStarted)
  //  {
  //    Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d" //PH34R MY MAD-LEET CODING SKILLZ
  //          "                                                       "
  //          "Stage %d", cgs.alienStage + 1, cgs.humanStage + 1);
  //  }
  //  else if (cg.snap->ps.stats[STAT_PTEAM] == PTE_ALIENS)
  //  {
  //    kills = cgs.alienNextStageThreshold - cgs.alienKills;
  //
  //    if (cgs.alienNextStageThreshold < 0)
  //      Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d", cgs.alienStage + 1);
  //    else if (kills == 1)
  //      Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage", cgs.alienStage + 1, kills);
  //    else
  //      Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage", cgs.alienStage + 1, kills);
  //  }
  //  else
  //  if (cg.snap->ps.stats[STAT_PTEAM] == PTE_HUMANS)
  //  {
  //    kills = cgs.humanNextStageThreshold - cgs.humanKills;
  //
  //    if (cgs.humanNextStageThreshold < 0)
  Com_sprintf(s, MAX_TOKEN_CHARS, "Survivors");
  //    else if (kills == 1)
  //      Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage", cgs.humanStage + 1, kills);
  //    else
  //      Com_sprintf(s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage", cgs.humanStage + 1, kills);
  //  }

  w = CG_Text_Width(s, scale, 0);

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - w;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + (rect->w / 2.0f) - (w / 2.0f);
      break;

    default:
      tx = 0.0f;
  }

  CG_Text_Paint(text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle);
}

/*
 ==================
 CG_DrawFPS
 ==================
 */
//TA: personally i think this should be longer - it should really be a cvar
#define FPS_FRAMES  20
#define FPS_STRING  "fps"
static void
CG_DrawFPS(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color, int align,
  int textStyle, qboolean scalableText)
{
  char *s;
  int tx, w, totalWidth, strLength;
  static int previousTimes[FPS_FRAMES];
  static int index;
  int i, total;
  int fps;
  static int previous;
  int t, frameTime;

  if (!cg_drawFPS.integer)
    return;

  // don't use serverTime, because that will be drifting to
  // correct for internet lag changes, timescales, timedemos, etc
  t = trap_Milliseconds();
  frameTime = t - previous;
  previous = t;

  previousTimes[index % FPS_FRAMES] = frameTime;
  index++;

  if (index > FPS_FRAMES)
  {
    // average multiple frames together to smooth changes out a bit
    total = 0;

    for(i = 0;i < FPS_FRAMES;i++)
      total += previousTimes[i];

    if (!total)
      total = 1;

    fps = 1000 * FPS_FRAMES / total;

    s = va("%d", fps);
    w = CG_Text_Width("0", scale, 0);
    strLength = CG_DrawStrlen(s);
    totalWidth = CG_Text_Width(FPS_STRING, scale, 0) + w * strLength;

    switch(align)
    {
      case ITEM_ALIGN_LEFT:
        tx = rect->x;
        break;

      case ITEM_ALIGN_RIGHT:
        tx = rect->x + rect->w - totalWidth;
        break;

      case ITEM_ALIGN_CENTER:
        tx = rect->x + (rect->w / 2.0f) - (totalWidth / 2.0f);
        break;

      default:
        tx = 0.0f;
    }

    if (scalableText)
    {
      for(i = 0;i < strLength;i++)
      {
        char c[2];

        c[0] = s[i];
        c[1] = '\0';

        CG_Text_Paint(text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle);
      }
    }
    else
    {
      trap_R_SetColor(color);
      CG_DrawField(rect->x, rect->y, 3, rect->w / 3, rect->h, fps);
      trap_R_SetColor(NULL);
    }

    if (scalableText)
      CG_Text_Paint(
        text_x + tx + i * w, rect->y + text_y, scale, color, FPS_STRING, 0, 0, textStyle);
  }
}

/*
 =================
 CG_DrawTimerMins
 =================
 */
static void
CG_DrawTimerMins(rectDef_t *rect, vec4_t color)
{
  int mins, seconds;
  int msec;

  if (!cg_drawTimer.integer)
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;

  trap_R_SetColor(color);
  CG_DrawField(rect->x, rect->y, 3, rect->w / 3, rect->h, mins);
  trap_R_SetColor(NULL);
}

/*
 =================
 CG_DrawTimerSecs
 =================
 */
static void
CG_DrawTimerSecs(rectDef_t *rect, vec4_t color)
{
  int mins, seconds;
  int msec;

  if (!cg_drawTimer.integer)
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;

  trap_R_SetColor(color);
  CG_DrawFieldPadded(rect->x, rect->y, 2, rect->w / 2, rect->h, seconds);
  trap_R_SetColor(NULL);
}
static void
CG_DrawZombieTimerBackground(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
}

static void
CG_DrawZombieRecordBackground(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
}

static void
CG_DrawExtraMediKits(rectDef_t *rect, vec4_t color, qhandle_t shader)
{
  playerState_t *ps;
  centity_t *cent;
  int kits;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  kits = ps->persistant[PERS_UNUSED];

  if (cgs.survivalRecords[0] < 1)
  {
    return;
  }

  if (kits == 0)
  {
    return;
  }
  if (kits > 0)
  {
    CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  }
  if (kits > 1)
  {
    CG_DrawPic(rect->x, rect->y + 25, rect->w, rect->h, shader);
  }
  if (kits > 2)
  {
    CG_DrawPic(rect->x, rect->y + 50, rect->w, rect->h, shader);
  }

  //CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);

}
static void
CG_DrawMedal(rectDef_t *rect, vec4_t color, qhandle_t shader, int medal)
{
  int currentRecord = -1;
  int offset = 0;
  
  if (cgs.survivalRecords[0] < 1)
  {
    return;
  }

  if (cg.time - cgs.levelStartTime < cgs.survivalRecords[0]) //A simple operand can change everything.
  {
    currentRecord = -1; //Havent Win any medal.
  }
  else if (cg.time - cgs.levelStartTime < cgs.survivalRecords[1])
  {
    currentRecord = 0; //Bronze
  }
  else if (cg.time - cgs.levelStartTime < cgs.survivalRecords[2])
  {
    currentRecord = 1; //Silver
  }
  else
  {
    currentRecord = 2; //gold
  }

  /*if(currentRecord == -1)
   return;*/

  if (currentRecord < medal)
    return;

  if (medal == 0 && currentRecord == 0)
  {
    offset = 78;
  }
  if (currentRecord == 1)
  {
    offset = 30;
  }
  if (currentRecord == 2)
  {
    offset = 30;
  }

  CG_DrawPic(rect->x - offset, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
}

static void
CG_DrawZombieTimer(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color,
  int align, int textStyle, qhandle_t shader)
{
  char *s;
  int i, tx, w, totalWidth, strLength;
  int mins, seconds, tens, ms, ums, dms;
  int msec;
  int currentRecord;

  vec4_t textColor, adjustedColor;

  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };
  
  if (cgs.survivalRecords[0] < 1)
  {
    return;
  }

  msec = cg.time - cgs.levelStartTime;

  ms = msec;
  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;
  tens = seconds / 10;
  seconds -= tens * 10;
  
  ms = msec - (mins * 60 * 1000) - (seconds * 1000) - (tens * 10 * 1000);
  
  dms = (ms / (999 / 99)) / 10;
  ums = (ms / (999 / 99)) % 10;

  s = va("%d%d:%d%d:%d%d", mins / 10, mins % 10, tens, seconds, dms, ums);
  
  w = CG_Text_Width("0", scale, 0);
  strLength = CG_DrawStrlen(s);
  totalWidth = w * strLength;

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - totalWidth;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + (rect->w / 2.0f) - (totalWidth / 2.0f);
      break;

    default:
      tx = 0.0f;
  }
  
  Vector4Copy( textColor, adjustedColor );
  adjustedColor[3] = 1.0f;

  // trap_R_SetColor( adjustedColor );
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);
  
  if (cg.time - cgs.levelStartTime < cgs.survivalRecords[0]) //A simple operand can change everything.
  {
    currentRecord = 0;
  }
  else if (cg.time - cgs.levelStartTime < cgs.survivalRecords[1])
  {
    currentRecord = 1;
  }
  else if (cg.time - cgs.levelStartTime < cgs.survivalRecords[2])
  {
    currentRecord = 2;
  }
  else
  {
    currentRecord = -1;
  }

  if (currentRecord > -1)
  {
    if (cgs.survivalRecords[currentRecord] - (cg.time - cgs.levelStartTime) < 10000)
    {
      if (dms == 0)
      {
        cg.fadeTimer = cg.time + 1000;
      }

      if (cg.time < cg.fadeTimer)
      {
        color[3] = ((cg.fadeTimer) - cg.time) / (1000.0f);
      }
      if (color[3] == 0)
      {
        color[3] = 1;
      }
    }
  }
  
  /*color = CG_FadeColor( fadeTimer, 1000 * cg_centertime.value );
   if( !color )
   return;*/

  for(i = 0;i < strLength;i++)
  {
    char c[2];

    c[0] = s[i];
    c[1] = '\0';
    
    CG_Text_Paint(text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle);
  }
}
static void
CG_DrawZombieRecord(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color,
  int align, int textStyle, qhandle_t shader)
{
  char *s;
  int i, tx, w, totalWidth, strLength;
  int mins, seconds, tens, ms, ums, dms;
  int msec;
  int currentRecord;

  vec4_t textColor, adjustedColor;
  
  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };
  
  //Some how this fucking piece of crap dont work... WHY? i dont know.

  if (cgs.survivalRecords[0] < 1)
  {
    return;
  }

  msec = cg.time - cgs.levelStartTime;
  
  if (msec < cgs.survivalRecords[0]) //A simple operand can change everything.
  {
    currentRecord = 0;
  }
  else if (msec < cgs.survivalRecords[1])
  {
    currentRecord = 1;
  }
  else if (msec < cgs.survivalRecords[2])
  {
    currentRecord = 2;
  }
  else
  {
    return;
  }

  //msec = currentRecord;
  

  ms = cgs.survivalRecords[currentRecord];
  seconds = cgs.survivalRecords[currentRecord] / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;
  tens = seconds / 10;
  seconds -= tens * 10;
  
  ms = cgs.survivalRecords[currentRecord] - (mins * 60 * 1000) - (seconds * 1000) - (tens * 10
      * 1000);
  
  dms = (ms / (999 / 99)) / 10;
  ums = (ms / (999 / 99)) % 10;

  s = va("%d%d:%d%d:%d%d", mins / 10, mins % 10, tens, seconds, dms, ums);
  
  w = CG_Text_Width("0", scale, 0);
  strLength = CG_DrawStrlen(s);
  totalWidth = w * strLength;

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - totalWidth;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + (rect->w / 2.0f) - (totalWidth / 2.0f);
      break;

    default:
      tx = 0.0f;
  }
  
  Vector4Copy( textColor, adjustedColor );
  adjustedColor[3] = 1.0f;

  //trap_R_SetColor( adjustedColor );
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
  trap_R_SetColor(NULL);

  for(i = 0;i < strLength;i++)
  {
    char c[2];

    c[0] = s[i];
    c[1] = '\0';

    CG_Text_Paint(text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle);
  }
}

/*
 =================
 CG_DrawTimer
 =================
 */
static void
CG_DrawTimer(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color, int align,
  int textStyle)
{
  char *s;
  int i, tx, w, totalWidth, strLength;
  int mins, seconds, tens;
  int msec;

  if (!cg_drawTimer.integer)
    return;

  msec = cg.time - cgs.levelStartTime;

  seconds = msec / 1000;
  mins = seconds / 60;
  seconds -= mins * 60;
  tens = seconds / 10;
  seconds -= tens * 10;

  s = va("%d:%d%d", mins, tens, seconds);
  w = CG_Text_Width("0", scale, 0);
  strLength = CG_DrawStrlen(s);
  totalWidth = w * strLength;

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - totalWidth;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + (rect->w / 2.0f) - (totalWidth / 2.0f);
      break;

    default:
      tx = 0.0f;
  }

  for(i = 0;i < strLength;i++)
  {
    char c[2];

    c[0] = s[i];
    c[1] = '\0';

    CG_Text_Paint(text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle);
  }
  return;
}

/*
 ==================
 CG_DrawSnapshot
 ==================
 */
static void
CG_DrawSnapshot(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t color, int align,
  int textStyle)
{
  char *s;
  int w, tx;

  if (!cg_drawSnapshot.integer)
    return;

  s = va(
    "time:%d snap:%d cmd:%d", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence);
  w = CG_Text_Width(s, scale, 0);

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      tx = rect->x;
      break;

    case ITEM_ALIGN_RIGHT:
      tx = rect->x + rect->w - w;
      break;

    case ITEM_ALIGN_CENTER:
      tx = rect->x + (rect->w / 2.0f) - (w / 2.0f);
      break;

    default:
      tx = 0.0f;
  }

  CG_Text_Paint(text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle);
}

/*
 ===============================================================================

 LAGOMETER

 ===============================================================================
 */

#define LAG_SAMPLES   128

typedef struct
{
  int frameSamples[LAG_SAMPLES];
  int frameCount;
  int snapshotFlags[LAG_SAMPLES];
  int snapshotSamples[LAG_SAMPLES];
  int snapshotCount;
} lagometer_t;

lagometer_t lagometer;

/*
 ==============
 CG_AddLagometerFrameInfo

 Adds the current interpolate / extrapolate bar for this frame
 ==============
 */
void
CG_AddLagometerFrameInfo(void)
{
  int offset;

  offset = cg.time - cg.latestSnapshotTime;
  lagometer.frameSamples[lagometer.frameCount & (LAG_SAMPLES - 1)] = offset;
  lagometer.frameCount++;
}

/*
 ==============
 CG_AddLagometerSnapshotInfo

 Each time a snapshot is received, log its ping time and
 the number of snapshots that were dropped before it.

 Pass NULL for a dropped packet.
 ==============
 */
void
CG_AddLagometerSnapshotInfo(snapshot_t *snap)
{
  // dropped packet
  if (!snap)
  {
    lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = -1;
    lagometer.snapshotCount++;
    return;
  }

  // add this snapshot's info
  lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->ping;
  lagometer.snapshotFlags[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->snapFlags;
  lagometer.snapshotCount++;
}

/*
 ==============
 CG_DrawDisconnect

 Should we draw something differnet for long lag vs no packets?
 ==============
 */
static void
CG_DrawDisconnect(void)
{
  float x, y;
  int cmdNum;
  usercmd_t cmd;
  const char *s;
  int w;
  vec4_t color =
  { 1.0f, 1.0f, 1.0f, 1.0f };

  // draw the phone jack if we are completely past our buffers
  cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
  trap_GetUserCmd(cmdNum, &cmd);

  // special check for map_restart
  if (cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time)
    return;

  // also add text in center of screen
  s = "Connection Interrupted";
  w = CG_Text_Width(s, 0.7f, 0);
  CG_Text_Paint(320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED);

  // blink the icon
  if ((cg.time >> 9) & 1)
    return;

  x = 640 - 48;
  y = 480 - 48;

  CG_DrawPic(x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga"));
}

#define MAX_LAGOMETER_PING  900
#define MAX_LAGOMETER_RANGE 300

#define PING_FRAMES 40

/*
 ==============
 CG_DrawLagometer
 ==============
 */
static void
CG_DrawLagometer(rectDef_t *rect, float text_x, float text_y, float scale, vec4_t textColor)
{
  int a, x, y, i;
  float v;
  float ax, ay, aw, ah, mid, range;
  int color;
  vec4_t adjustedColor;
  float vscale;
  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };

  if (cg.snap->ps.pm_type == PM_INTERMISSION)
    return;

  if (!cg_lagometer.integer)
    return;

  if (cg.demoPlayback)
    return;

  Vector4Copy( textColor, adjustedColor );
  adjustedColor[3] = 0.25f;

  trap_R_SetColor(adjustedColor);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader);
  trap_R_SetColor(NULL);

  //
  // draw the graph
  //
  ax = x = rect->x;
  ay = y = rect->y;
  aw = rect->w;
  ah = rect->h;

  trap_R_SetColor(NULL);

  CG_AdjustFrom640(&ax, &ay, &aw, &ah);

  color = -1;
  range = ah / 3;
  mid = ay + range;

  vscale = range / MAX_LAGOMETER_RANGE;

  // draw the frame interpoalte / extrapolate graph
  for(a = 0;a < aw;a++)
  {
    i = (lagometer.frameCount - 1 - a) & (LAG_SAMPLES - 1);
    v = lagometer.frameSamples[i];
    v *= vscale;

    if (v > 0)
    {
      if (color != 1)
      {
        color = 1;
        trap_R_SetColor(g_color_table[ColorIndex( COLOR_YELLOW )]);
      }

      if (v > range)
        v = range;

      trap_R_DrawStretchPic(ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
    }
    else if (v < 0)
    {
      if (color != 2)
      {
        color = 2;
        trap_R_SetColor(g_color_table[ColorIndex( COLOR_BLUE )]);
      }

      v = -v;
      if (v > range)
        v = range;

      trap_R_DrawStretchPic(ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
    }
  }

  // draw the snapshot latency / drop graph
  range = ah / 2;
  vscale = range / MAX_LAGOMETER_PING;

  for(a = 0;a < aw;a++)
  {
    i = (lagometer.snapshotCount - 1 - a) & (LAG_SAMPLES - 1);
    v = lagometer.snapshotSamples[i];

    if (v > 0)
    {
      if (lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED)
      {
        if (color != 5)
        {
          color = 5; // YELLOW for rate delay
          trap_R_SetColor(g_color_table[ColorIndex( COLOR_YELLOW )]);
        }
      }
      else
      {
        if (color != 3)
        {
          color = 3;

          trap_R_SetColor(g_color_table[ColorIndex( COLOR_GREEN )]);
        }
      }

      v = v * vscale;

      if (v > range)
        v = range;

      trap_R_DrawStretchPic(ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader);
    }
    else if (v < 0)
    {
      if (color != 4)
      {
        color = 4; // RED for dropped snapshots
        trap_R_SetColor(g_color_table[ColorIndex( COLOR_RED )]);
      }

      trap_R_DrawStretchPic(
        ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader);
    }
  }

  trap_R_SetColor(NULL);

  if (cg_nopredict.integer || cg_synchronousClients.integer)
    CG_Text_Paint(ax, ay, 0.5, white, "snc", 0, 0, ITEM_TEXTSTYLE_NORMAL);
  else
  {
    static int previousPings[PING_FRAMES];
    static int index;
    int i, ping = 0;
    char *s;

    previousPings[index++] = cg.snap->ping;
    index = index % PING_FRAMES;

    for(i = 0;i < PING_FRAMES;i++)
      ping += previousPings[i];

    ping /= PING_FRAMES;

    s = va("%d", ping);
    ax = rect->x + (rect->w / 2.0f) - (CG_Text_Width(s, scale, 0) / 2.0f) + text_x;
    ay = rect->y + (rect->h / 2.0f) + (CG_Text_Height(s, scale, 0) / 2.0f) + text_y;

    Vector4Copy( textColor, adjustedColor );
    adjustedColor[3] = 0.5f;
    CG_Text_Paint(ax, ay, scale, adjustedColor, s, 0, 0, ITEM_TEXTSTYLE_NORMAL);
  }

  CG_DrawDisconnect();
}

/*
 ==============
 CG_DrawTextBlock
 ==============
 */
static void
CG_DrawTextBlock(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle, const char *text, menuDef_t *parent, itemDef_t *textItem)
{
  float x, y, w, h;

  //offset the text

  //Survival text must be shorter 290
  if (cgs.survivalRecords[0] > 0)
  {
    rect->w = 290;
  }

  x = rect->x;
  y = rect->y;
  w = rect->w - (16 + (2 * text_x)); //16 to ensure text within frame
  h = rect->h;

  textItem->text = text;

  textItem->parent = parent;
  memcpy(textItem->window.foreColor, color, sizeof(vec4_t));
  textItem->window.flags = 0;

  switch(align)
  {
    case ITEM_ALIGN_LEFT:
      textItem->window.rect.x = x;
      break;

    case ITEM_ALIGN_RIGHT:
      textItem->window.rect.x = x + w;
      break;

    case ITEM_ALIGN_CENTER:
      textItem->window.rect.x = x + (w / 2);
      break;

    default:
      textItem->window.rect.x = x;
      break;
  }

  textItem->window.rect.y = y;
  textItem->window.rect.w = w;
  textItem->window.rect.h = h;
  textItem->window.borderSize = 0;
  textItem->textRect.x = 0;
  textItem->textRect.y = 0;
  textItem->textRect.w = 0;
  textItem->textRect.h = 0;
  textItem->textalignment = align;
  textItem->textalignx = text_x;
  textItem->textaligny = text_y;
  textItem->textscale = scale;
  textItem->textStyle = textStyle;

  //hack to utilise existing autowrap code
  Item_Text_AutoWrapped_Paint(textItem);
}

/*
 ===================
 CG_DrawConsole
 ===================
 */
static void
CG_DrawConsole(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle)
{
  static menuDef_t dummyParent;
  static itemDef_t textItem;

  CG_DrawTextBlock(
    rect, text_x, text_y, color, scale, align, textStyle, cg.consoleText, &dummyParent, &textItem);
}

/*
 ===================
 CG_DrawTutorial
 ===================
 */
static void
CG_DrawTutorial(rectDef_t *rect, float text_x, float text_y, vec4_t color, float scale, int align,
  int textStyle)
{
  static menuDef_t dummyParent;
  static itemDef_t textItem;

  if (!cg_tutorial.integer)
    return;

  CG_DrawTextBlock(
    rect, text_x, text_y, color, scale, align, textStyle, CG_TutorialText(), &dummyParent,
    &textItem);
}

/*
 ===================
 CG_DrawWeaponIcon
 ===================
 */
void
CG_DrawWeaponIcon(rectDef_t *rect, vec4_t color)
{
  int ammo, clips, maxAmmo;
  centity_t *cent;
  playerState_t *ps;

  cent = &cg_entities[cg.snap->ps.clientNum];
  ps = &cg.snap->ps;

  BG_UnpackAmmoArray(cent->currentState.weapon, &ps->ammo, ps->powerups, &ammo, &clips);
  BG_FindAmmoForWeapon(cent->currentState.weapon, &maxAmmo, NULL);

  // don't display if dead
  if (cg.predictedPlayerState.stats[STAT_HEALTH] <= 0)
    return;

  if (cent->currentState.weapon == 0)
    return;

  CG_RegisterWeapon(cent->currentState.weapon);

  if (clips == 0 && !BG_FindInfinteAmmoForWeapon(cent->currentState.weapon))
  {
    float ammoPercent = (float) ammo / (float) maxAmmo;

    if (ammoPercent < 0.33f)
    {
      color[0] = 1.0f;
      color[1] = color[2] = 0.0f;
    }
  }

  if (cg.predictedPlayerState.stats[STAT_PTEAM] == PTE_ALIENS && CG_AtHighestClass())
  {
    if (cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME)
    {
      if (((cg.time - cg.lastEvolveAttempt) / 300) % 2)
        color[3] = 0.0f;
    }
  }

  trap_R_SetColor(color);
  CG_DrawPic(rect->x, rect->y, rect->w, rect->h, cg_weapons[cent->currentState.weapon].weaponIcon);
  trap_R_SetColor(NULL);
}

/*
 ================================================================================

 CROSSHAIR

 ================================================================================
 */

/*
 =================
 CG_DrawCrosshair
 =================
 */
static void
CG_DrawCrosshair(void)
{
  float w, h;
  qhandle_t hShader;
  float x, y;
  weaponInfo_t *wi;

  if (!cg_drawCrosshair.integer)
    return;

  if ((cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) || (cg.snap->ps.stats[STAT_STATE]
      & SS_INFESTING) || (cg.snap->ps.stats[STAT_STATE] & SS_HOVELING))
    return;

  /*if( cg.renderingThirdPerson )
   return;
   */
  wi = &cg_weapons[cg.snap->ps.weapon];

  w = h = wi->crossHairSize;

  x = cg_crosshairX.integer;
  y = cg_crosshairY.integer;
  CG_AdjustFrom640(&x, &y, &w, &h);

  hShader = wi->crossHair;

  if (hShader != 0)
  {
    trap_R_DrawStretchPic(x + cg.refdef.x + 0.5 * (cg.refdef.width - w), y + cg.refdef.y + 0.5
        * (cg.refdef.height - h), w, h, 0, 0, 1, 1, hShader);
  }
}

/*
 =================
 CG_ScanForCrosshairEntity
 =================
 */
static void
CG_ScanForCrosshairEntity(void)
{
  trace_t trace;
  vec3_t start, end;
  int content;
  pTeam_t team;

  VectorCopy( cg.refdef.vieworg, start );
  VectorMA( start, 131072, cg.refdef.viewaxis[ 0 ], end );

  CG_Trace(&trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID
      | CONTENTS_BODY);

  if (trace.entityNum >= MAX_CLIENTS)
    return;

  // if the player is in fog, don't show it
//  content = trap_CM_PointContents(trace.endpos, 0);
//  if (content & CONTENTS_FOG)
//    return;

  team = cgs.clientinfo[trace.entityNum].team;

  if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR)
  {
    //only display team names of those on the same team as this player
    if (team != cg.snap->ps.stats[STAT_PTEAM])
      return;
  }

  // update the fade timer
  cg.crosshairClientNum = trace.entityNum;
  cg.crosshairClientTime = cg.time;
}

/*
 =====================
 CG_DrawCrosshairNames
 =====================
 */
static void
CG_DrawCrosshairNames(rectDef_t *rect, float scale, int textStyle)
{
  float *color;
  char *name;
  float w, x;

  if (!cg_drawCrosshair.integer)
    return;

  if (!cg_drawCrosshairNames.integer)
    return;

  /*if( cg.renderingThirdPerson )
   return;*/

  // scan the known entities to see if the crosshair is sighted on one
  CG_ScanForCrosshairEntity();

  // draw the name of the player being looked at
  color = CG_FadeColor(cg.crosshairClientTime, 1000);
  if (!color)
  {
    trap_R_SetColor(NULL);
    return;
  }

  name = cgs.clientinfo[cg.crosshairClientNum].name;
  w = CG_Text_Width(name, scale, 0);
  x = rect->x + rect->w / 2;
  CG_Text_Paint(x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle);
  trap_R_SetColor(NULL);
}

/*
 ===============
 CG_OwnerDraw

 Draw an owner drawn item
 ===============
 */
void
CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw,
  int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader,
  int textStyle)
{
  rectDef_t rect;

  if (cg_drawStatus.integer == 0)
    return;

  rect.x = x;
  rect.y = y;
  rect.w = w;
  rect.h = h;

  switch(ownerDraw)
  {
    case CG_PLAYER_CREDITS_VALUE:
      CG_DrawPlayerCreditsValue(&rect, color, qtrue);
      break;
    case CG_PLAYER_BANK_VALUE:
      CG_DrawPlayerBankValue(&rect, color, qtrue);
      break;
    case CG_PLAYER_CREDITS_VALUE_NOPAD:
      CG_DrawPlayerCreditsValue(&rect, color, qfalse);
      break;
    case CG_PLAYER_BANK_VALUE_NOPAD:
      CG_DrawPlayerBankValue(&rect, color, qfalse);
      break;
    case CG_PLAYER_STAMINA:
      CG_DrawPlayerStamina(&rect, color, scale, align, textStyle, special);
      break;
    case CG_PLAYER_STAMINA_1:
      CG_DrawPlayerStamina1(&rect, color, shader);
      break;
    case CG_PLAYER_STAMINA_2:
      CG_DrawPlayerStamina2(&rect, color, shader);
      break;
    case CG_PLAYER_STAMINA_3:
      CG_DrawPlayerStamina3(&rect, color, shader);
      break;
    case CG_PLAYER_STAMINA_4:
      CG_DrawPlayerStamina4(&rect, color, shader);
      break;
    case CG_PLAYER_STAMINA_BOLT:
      CG_DrawPlayerStaminaBolt(&rect, color, shader);
      break;
    case CG_PLAYER_AMMO_VALUE:
      CG_DrawPlayerAmmoValue(&rect, color);
      break;
    case CG_PLAYER_CLIPS_VALUE:
      CG_DrawPlayerClipsValue(&rect, color);
      break;
    case CG_PLAYER_BUILD_TIMER:
      CG_DrawPlayerBuildTimer(&rect, color);
      break;
    case CG_PLAYER_HEALTH:
      CG_DrawPlayerHealthValue(&rect, color);
      break;
    case CG_PLAYER_HEALTH_BAR:
      CG_DrawPlayerHealthBar(&rect, color, scale, align, textStyle, special);
      break;
    case CG_PLAYER_HEALTH_CROSS:
      CG_DrawPlayerHealthCross(&rect, color, shader);
      break;
    case CG_PLAYER_CLIPS_RING:
      CG_DrawPlayerClipsRing(&rect, color, shader);
      break;
    case CG_PLAYER_BUILD_TIMER_RING:
      CG_DrawPlayerBuildTimerRing(&rect, color, shader);
      break;
    case CG_PLAYER_WALLCLIMBING:
      CG_DrawPlayerWallclimbing(&rect, color, shader);
      break;
    case CG_PLAYER_BOOSTED:
      CG_DrawPlayerBoosted(&rect, color, shader);
      break;
    case CG_PLAYER_BOOST_BOLT:
      CG_DrawPlayerBoosterBolt(&rect, color, shader);
      break;
    case CG_PLAYER_POISON_BARBS:
      CG_DrawPlayerPoisonBarbs(&rect, color, shader);
      break;
    case CG_PLAYER_ALIEN_SENSE:
      CG_DrawAlienSense(&rect);
      break;
    case CG_PLAYER_HUMAN_SCANNER:
      CG_DrawHumanScanner(&rect, shader, color);
      break;
    case CG_PLAYER_USABLE_BUILDABLE:
      CG_DrawUsableBuildable(&rect, shader, color);
      break;
    case CG_AREA_SYSTEMCHAT:
      CG_DrawAreaSystemChat(&rect, scale, color, shader);
      break;
    case CG_AREA_TEAMCHAT:
      CG_DrawAreaTeamChat(&rect, scale, color, shader);
      break;
    case CG_AREA_CHAT:
      CG_DrawAreaChat(&rect, scale, color, shader);
      break;
    case CG_KILLER:
      CG_DrawKiller(&rect, scale, color, shader, textStyle);
      break;
    case CG_PLAYER_SELECT:
      CG_DrawItemSelect(&rect, color);
      break;
    case CG_PLAYER_WEAPONICON:
      CG_DrawWeaponIcon(&rect, color);
      break;
    case CG_PLAYER_SELECTTEXT:
      CG_DrawItemSelectText(&rect, scale, textStyle);
      break;
    case CG_SPECTATORS:
      CG_DrawTeamSpectators(&rect, scale, color, shader);
      break;
    case CG_PLAYER_CROSSHAIRNAMES:
      CG_DrawCrosshairNames(&rect, scale, textStyle);
      break;
    case CG_STAGE_REPORT_TEXT:
      CG_DrawStageReport(&rect, text_x, text_y, color, scale, align, textStyle);
      break;

      //loading screen
    case CG_LOAD_LEVELSHOT:
      CG_DrawLevelShot(&rect);
      break;
    case CG_LOAD_MEDIA:
      CG_DrawMediaProgress(&rect, color, scale, align, textStyle, special);
      break;
    case CG_LOAD_MEDIA_LABEL:
      CG_DrawMediaProgressLabel(&rect, text_x, text_y, color, scale, align);
      break;
    case CG_LOAD_BUILDABLES:
      CG_DrawBuildablesProgress(&rect, color, scale, align, textStyle, special);
      break;
    case CG_LOAD_BUILDABLES_LABEL:
      CG_DrawBuildablesProgressLabel(&rect, text_x, text_y, color, scale, align);
      break;
    case CG_LOAD_CHARMODEL:
      CG_DrawCharModelProgress(&rect, color, scale, align, textStyle, special);
      break;
    case CG_LOAD_CHARMODEL_LABEL:
      CG_DrawCharModelProgressLabel(&rect, text_x, text_y, color, scale, align);
      break;
    case CG_LOAD_OVERALL:
      CG_DrawOverallProgress(&rect, color, scale, align, textStyle, special);
      break;
    case CG_LOAD_LEVELNAME:
      CG_DrawLevelName(&rect, text_x, text_y, color, scale, align, textStyle);
      break;
    case CG_LOAD_MOTD:
      CG_DrawMOTD(&rect, text_x, text_y, color, scale, align, textStyle);
      break;
    case CG_LOAD_HOSTNAME:
      CG_DrawHostname(&rect, text_x, text_y, color, scale, align, textStyle);
      break;

    case CG_FPS:
      CG_DrawFPS(&rect, text_x, text_y, scale, color, align, textStyle, qtrue);
      break;
    case CG_FPS_FIXED:
      CG_DrawFPS(&rect, text_x, text_y, scale, color, align, textStyle, qfalse);
      break;
    case CG_TIMER:
      CG_DrawTimer(&rect, text_x, text_y, scale, color, align, textStyle);
      break;
    case CG_TIMER_MINS:
      CG_DrawTimerMins(&rect, color);
      break;
    case CG_TIMER_SECS:
      CG_DrawTimerSecs(&rect, color);
      break;
    case CG_SNAPSHOT:
      CG_DrawSnapshot(&rect, text_x, text_y, scale, color, align, textStyle);
      break;
    case CG_LAGOMETER:
      CG_DrawLagometer(&rect, text_x, text_y, scale, color);
      break;
      
      //zombieSurvival
    case CG_ZOMBIETIMER:
      CG_DrawZombieTimer(&rect, text_x, text_y, scale, color, align, textStyle, shader);
      break;
    case CG_ZOMBIERECORD:
      CG_DrawZombieRecord(&rect, text_x, text_y, scale, color, align, textStyle, shader);
      break;
      
    case CG_ZOMBIETIMERBACKGROUND:
      CG_DrawZombieTimerBackground(&rect, color, shader);
      break;
      
    case CG_ZOMBIERECORDBACKGROUND:
      CG_DrawZombieRecordBackground(&rect, color, shader);
      break;
      //Survival Medals
    case CG_GOLDMEDAL:
      CG_DrawMedal(&rect, color, shader, 2);
      break;

    case CG_SILVERMEDAL:
      CG_DrawMedal(&rect, color, shader, 1);
      break;
      
    case CG_BRONZEMEDAL:
      CG_DrawMedal(&rect, color, shader, 0);
      break;

    case CG_EXTRAMEDIKIT:
      CG_DrawExtraMediKits(&rect, color, shader);
      break;

    case CG_DEMO_PLAYBACK:
      CG_DrawDemoPlayback(&rect, color, shader);
      break;
    case CG_DEMO_RECORDING:
      CG_DrawDemoRecording(&rect, color, shader);
      break;

    case CG_CONSOLE:
      CG_DrawConsole(&rect, text_x, text_y, color, scale, align, textStyle);
      break;

    case CG_TUTORIAL:
      CG_DrawTutorial(&rect, text_x, text_y, color, scale, align, textStyle);
      break;

    default:
      break;
  }
}

void
CG_MouseEvent(int x, int y)
{
  int n;

  if ((cg.predictedPlayerState.pm_type == PM_NORMAL || cg.predictedPlayerState.pm_type
      == PM_SPECTATOR) && cg.showScores == qfalse)
  {
    trap_Key_SetCatcher(0);
    return;
  }

  cgs.cursorX += x;
  if (cgs.cursorX < 0)
    cgs.cursorX = 0;
  else if (cgs.cursorX > 640)
    cgs.cursorX = 640;

  cgs.cursorY += y;
  if (cgs.cursorY < 0)
    cgs.cursorY = 0;
  else if (cgs.cursorY > 480)
    cgs.cursorY = 480;

  n = Display_CursorType(cgs.cursorX, cgs.cursorY);
  cgs.activeCursor = 0;
  if (n == CURSOR_ARROW)
    cgs.activeCursor = cgs.media.selectCursor;
  else if (n == CURSOR_SIZER)
    cgs.activeCursor = cgs.media.sizeCursor;

  if (cgs.capturedItem)
    Display_MouseMove(cgs.capturedItem, x, y);
  else
    Display_MouseMove(NULL, cgs.cursorX, cgs.cursorY);
}

/*
 ==================
 CG_HideTeamMenus
 ==================

 */
void
CG_HideTeamMenu(void)
{
  Menus_CloseByName("teamMenu");
  Menus_CloseByName("getMenu");
}

/*
 ==================
 CG_ShowTeamMenus
 ==================

 */
void
CG_ShowTeamMenu(void)
{
  Menus_OpenByName("teamMenu");
}

/*
 ==================
 CG_EventHandling
 ==================
 type 0 - no event handling
 1 - team menu
 2 - hud editor

 */
void
CG_EventHandling(int type)
{
  cgs.eventHandling = type;

  if (type == CGAME_EVENT_NONE)
    CG_HideTeamMenu();
}

void
CG_KeyEvent(int key, qboolean down)
{
  if (!down)
    return;

  if (cg.predictedPlayerState.pm_type == PM_NORMAL || (cg.predictedPlayerState.pm_type
      == PM_SPECTATOR && cg.showScores == qfalse))
  {
    CG_EventHandling(CGAME_EVENT_NONE);
    trap_Key_SetCatcher(0);
    return;
  }

  Display_HandleKey(key, down, cgs.cursorX, cgs.cursorY);

  if (cgs.capturedItem)
    cgs.capturedItem = NULL;
  else
  {
    if (key == K_MOUSE2 && down)
      cgs.capturedItem = Display_CaptureItem(cgs.cursorX, cgs.cursorY);
  }
}

int
CG_ClientNumFromName(const char *p)
{
  int i;

  for(i = 0;i < cgs.maxclients;i++)
  {
    if (cgs.clientinfo[i].infoValid && Q_stricmp(cgs.clientinfo[i].name, p) == 0)
      return i;
  }

  return -1;
}

void
CG_RunMenuScript(char **args)
{
}

void
CG_GetTeamColor(vec4_t *color)
{
  (*color)[0] = (*color)[2] = 0.0f;
  (*color)[1] = 0.17f;
  (*color)[3] = 0.25f;
}
//END TA UI


/*
 ================
 CG_DrawLighting

 ================
 */
static void
CG_DrawLighting(void)
{
  centity_t *cent;

  cent = &cg_entities[cg.snap->ps.clientNum];

  //fade to black if stamina is low
  if ((cg.snap->ps.stats[STAT_STAMINA] < -800) && (cg.snap->ps.stats[STAT_PTEAM] == PTE_HUMANS))
  {
    vec4_t black =
    { 0, 0, 0, 0 };
    black[3] = 1.0 - ((float) (cg.snap->ps.stats[STAT_STAMINA] + 1000) / 200.0f);
    trap_R_SetColor(black);
    CG_DrawPic(0, 0, 640, 480, cgs.media.whiteShader);
    trap_R_SetColor(NULL);
  }
}

/*
 ===============================================================================

 CENTER PRINTING

 ===============================================================================
 */

/*
 ==============
 CG_CenterPrint

 Called for important messages that should stay in the center of the screen
 for a few moments
 ==============
 */
void
CG_CenterPrint(const char *str, int y, int charWidth)
{
  char *s;

  Q_strncpyz(cg.centerPrint, str, sizeof(cg.centerPrint));

  cg.centerPrintTime = cg.time;
  cg.centerPrintY = y;
  cg.centerPrintCharWidth = charWidth;

  // count the number of lines for centering
  cg.centerPrintLines = 1;
  s = cg.centerPrint;
  while(*s)
  {
    if (*s == '\n')
      cg.centerPrintLines++;

    s++;
  }
}

/*
 ===================
 CG_DrawCenterString
 ===================
 */
static void
CG_DrawCenterString(void)
{
  char *start;
  int l;
  int x, y, w;
  int h;
  float *color;

  if (!cg.centerPrintTime)
    return;

  color = CG_FadeColor(cg.centerPrintTime, 1000 * cg_centertime.value);
  if (!color)
    return;

  trap_R_SetColor(color);

  start = cg.centerPrint;

  y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;

  while(1)
  {
    char linebuffer[1024];

    for(l = 0;l < 50;l++)
    {
      if (!start[l] || start[l] == '\n')
        break;

      linebuffer[l] = start[l];
    }

    linebuffer[l] = 0;

    w = CG_Text_Width(linebuffer, 0.5, 0);
    h = CG_Text_Height(linebuffer, 0.5, 0);
    x = (SCREEN_WIDTH - w) / 2;
    CG_Text_Paint(x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
    y += h + 6;

    while(*start && (*start != '\n'))
      start++;

    if (!*start)
      break;

    start++;
  }

  trap_R_SetColor(NULL);
}

//==============================================================================

//FIXME: both vote notes are hardcoded, change to ownerdrawn?

/*
 =================
 CG_DrawVote
 =================
 */
static void
CG_DrawVote(void)
{
  char *s;
  int sec;
  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };

  if (!cgs.voteTime)
    return;

  // play a talk beep whenever it is modified
  if (cgs.voteModified)
  {
    cgs.voteModified = qfalse;
    trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND);
  }

  sec = (VOTE_TIME - (cg.time - cgs.voteTime)) / 1000;

  if (sec < 0)
    sec = 0;

  s = va("VOTE(%i): \"%s\"  Yes:%i No:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo);
  CG_Text_Paint(8, 340, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL);
}

/*
 =================
 CG_DrawTeamVote
 =================
 */
static void
CG_DrawTeamVote(void)
{
  char *s;
  int sec, cs_offset;
  vec4_t white =
  { 1.0f, 1.0f, 1.0f, 1.0f };

  if (cg.predictedPlayerState.stats[STAT_PTEAM] == PTE_HUMANS)
    cs_offset = 0;
  else if (cg.predictedPlayerState.stats[STAT_PTEAM] == PTE_ALIENS)
    cs_offset = 1;
  else
    return;

  if (!cgs.teamVoteTime[cs_offset])
    return;

  // play a talk beep whenever it is modified
  if (cgs.teamVoteModified[cs_offset])
  {
    cgs.teamVoteModified[cs_offset] = qfalse;
    trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND);
  }

  sec = (VOTE_TIME - (cg.time - cgs.teamVoteTime[cs_offset])) / 1000;

  if (sec < 0)
    sec = 0;

  s = va(
    "TEAMVOTE(%i): \"%s\"  Yes:%i No:%i", sec, cgs.teamVoteString[cs_offset],
    cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset]);

  CG_Text_Paint(8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL);
}

static qboolean
CG_DrawScoreboard(void)
{
  static qboolean firstTime = qtrue;
  float fade, *fadeColor;

  if (menuScoreboard)
    menuScoreboard->window.flags &= ~WINDOW_FORCED;

  if (cg_paused.integer)
  {
    cg.deferredPlayerLoading = 0;
    firstTime = qtrue;
    return qfalse;
  }

  if (cg.showScores || cg.predictedPlayerState.pm_type == PM_INTERMISSION)
  {
    fade = 1.0;
    fadeColor = colorWhite;
  }
  else
  {
    cg.deferredPlayerLoading = 0;
    cg.killerName[0] = 0;
    firstTime = qtrue;
    return qfalse;
  }

  if (menuScoreboard == NULL)
    menuScoreboard = Menus_FindByName("teamscore_menu");

  if (menuScoreboard)
  {
    if (firstTime)
    {
      CG_SetScoreSelection(menuScoreboard);
      firstTime = qfalse;
    }

    Menu_Paint(menuScoreboard, qtrue);
  }

  return qtrue;
}

/*
 =================
 CG_DrawIntermission
 =================
 */
static void
CG_DrawIntermission(void)
{
  if (cg_drawStatus.integer)
    Menu_Paint(Menus_FindByName("default_hud"), qtrue);

  cg.scoreFadeTime = cg.time;
  cg.scoreBoardShowing = CG_DrawScoreboard();
}

#define FOLLOWING_STRING "following "

/*
 =================
 CG_DrawFollow
 =================
 */
static qboolean
CG_DrawFollow(void)
{
  float w;
  vec4_t color;
  char buffer[MAX_STRING_CHARS];

  if (!(cg.snap->ps.pm_flags & PMF_FOLLOW))
    return qfalse;

  color[0] = 1;
  color[1] = 1;
  color[2] = 1;
  color[3] = 1;

  strcpy(buffer, FOLLOWING_STRING);
  strcat(buffer, cgs.clientinfo[cg.snap->ps.clientNum].name);

  w = CG_Text_Width(buffer, 0.7f, 0);
  CG_Text_Paint(320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED);

  return qtrue;
}

/*
 =================
 CG_DrawQueue
 =================
 */
static qboolean
CG_DrawQueue(void)
{
  float w;
  vec4_t color;
  char buffer[MAX_STRING_CHARS];

  if (!(cg.snap->ps.pm_flags & PMF_QUEUED))
    return qfalse;

  color[0] = 1;
  color[1] = 1;
  color[2] = 1;
  color[3] = 1;

  Com_sprintf(
    buffer, MAX_STRING_CHARS, "You are in position %d of the spawn queue.",
    cg.snap->ps.persistant[PERS_QUEUEPOS] + 1);

  w = CG_Text_Width(buffer, 0.7f, 0);
  CG_Text_Paint(320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED);

  if (cg.snap->ps.stats[STAT_PTEAM] == PTE_ALIENS)
  {
    if (cgs.numAlienSpawns == 1)
      Com_sprintf(buffer, MAX_STRING_CHARS, "There is 1 spawn remaining.");
    else
      Com_sprintf(buffer, MAX_STRING_CHARS, "There are %d spawns remaining.", cgs.numAlienSpawns);
  }
  else if (cg.snap->ps.stats[STAT_PTEAM] == PTE_HUMANS)
  {
    if (cgs.numHumanSpawns == 1)
      Com_sprintf(buffer, MAX_STRING_CHARS, "There is 1 spawn remaining.");
    else
      Com_sprintf(buffer, MAX_STRING_CHARS, "There are %d spawns remaining.", cgs.numHumanSpawns);
  }

  w = CG_Text_Width(buffer, 0.7f, 0);
  CG_Text_Paint(320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED);

  return qtrue;
}

//==================================================================================

#define SPECTATOR_STRING "SPECTATOR"
/*
 =================
 CG_Draw2D
 =================
 */
static void
CG_Draw2D(void)
{
  vec4_t color;
  float w;
  menuDef_t *menu = NULL, *defaultMenu;

  color[0] = color[1] = color[2] = color[3] = 1.0f;

  // if we are taking a levelshot for the menu, don't draw anything
  if (cg.levelShot)
    return;

  if (cg_draw2D.integer == 0)
    return;

  if (cg.snap->ps.pm_type == PM_INTERMISSION)
  {
    CG_DrawIntermission();
    return;
  }

  //TA: draw the lighting effects e.g. nvg
  CG_DrawLighting();

  defaultMenu = Menus_FindByName("default_hud");

  if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR)
  {
    w = CG_Text_Width(SPECTATOR_STRING, 0.7f, 0);
    CG_Text_Paint(320 - w / 2, 440, 0.7f, color, SPECTATOR_STRING, 0, 0, ITEM_TEXTSTYLE_SHADOWED);
  }
  else
  {
    menu = Menus_FindByName(BG_FindHudNameForClass(cg.predictedPlayerState.stats[STAT_PCLASS]));
  }

  if (!(cg.snap->ps.stats[STAT_STATE] & SS_INFESTING) && !(cg.snap->ps.stats[STAT_STATE]
      & SS_HOVELING) && menu && (cg.snap->ps.stats[STAT_HEALTH] > 0))
  {
    if (cg_drawStatus.integer)
      Menu_Paint(menu, qtrue);

    CG_DrawCrosshair();
  }
  else if (cg_drawStatus.integer)
    Menu_Paint(defaultMenu, qtrue);

  CG_DrawVote();
  CG_DrawTeamVote();
  CG_DrawFollow();
  CG_DrawQueue();

  // don't draw center string if scoreboard is up
  cg.scoreBoardShowing = CG_DrawScoreboard();

  if (!cg.scoreBoardShowing)
    CG_DrawCenterString();
}

/*
 ===============
 CG_ScalePainBlendTCs
 ===============
 */
static void
CG_ScalePainBlendTCs(float* s1, float *t1, float *s2, float *t2)
{
  *s1 -= 0.5f;
  *t1 -= 0.5f;
  *s2 -= 0.5f;
  *t2 -= 0.5f;

  *s1 *= cg_painBlendZoom.value;
  *t1 *= cg_painBlendZoom.value;
  *s2 *= cg_painBlendZoom.value;
  *t2 *= cg_painBlendZoom.value;

  *s1 += 0.5f;
  *t1 += 0.5f;
  *s2 += 0.5f;
  *t2 += 0.5f;
}

#define PAINBLEND_BORDER    0.15f

/*
 ===============
 CG_PainBlend
 ===============
 */
static void
CG_PainBlend(void)
{
  vec4_t color;
  int damage;
  float damageAsFracOfMax;
  qhandle_t shader = cgs.media.viewBloodShader;
  float x, y, w, h;
  float s1, t1, s2, t2;

  if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR)
    return;

  damage = cg.lastHealth - cg.snap->ps.stats[STAT_HEALTH];

  if (damage < 0)
    damage = 0;

  damageAsFracOfMax = (float) damage / cg.snap->ps.stats[STAT_MAX_HEALTH];
  cg.lastHealth = cg.snap->ps.stats[STAT_HEALTH];

  cg.painBlendValue += damageAsFracOfMax * cg_painBlendScale.value;

  if (cg.painBlendValue > 0.0f)
  {
    cg.painBlendValue -= (cg.frametime / 1000.0f) * cg_painBlendDownRate.value;
  }

  if (cg.painBlendValue > 1.0f)
    cg.painBlendValue = 1.0f;
  else if (cg.painBlendValue <= 0.0f)
  {
    cg.painBlendValue = 0.0f;
    return;
  }

  if (cg.snap->ps.stats[STAT_PTEAM] == PTE_ALIENS)
    VectorSet( color, 0.43f, 0.8f, 0.37f );
  else if (cg.snap->ps.stats[STAT_PTEAM] == PTE_HUMANS)
    VectorSet( color, 0.8f, 0.0f, 0.0f );

  if (cg.painBlendValue > cg.painBlendTarget)
  {
    cg.painBlendTarget += (cg.frametime / 1000.0f) * cg_painBlendUpRate.value;
  }
  else if (cg.painBlendValue < cg.painBlendTarget)
    cg.painBlendTarget = cg.painBlendValue;

  if (cg.painBlendTarget > cg_painBlendMax.value)
    cg.painBlendTarget = cg_painBlendMax.value;

  color[3] = cg.painBlendTarget;

  trap_R_SetColor(color);

  //left
  x = 0.0f;
  y = 0.0f;
  w = PAINBLEND_BORDER * 640.0f;
  h = 480.0f;
  CG_AdjustFrom640(&x, &y, &w, &h);
  s1 = 0.0f;
  t1 = 0.0f;
  s2 = PAINBLEND_BORDER;
  t2 = 1.0f;
  CG_ScalePainBlendTCs(&s1, &t1, &s2, &t2);
  trap_R_DrawStretchPic(x, y, w, h, s1, t1, s2, t2, shader);

  //right
  x = 640.0f - (PAINBLEND_BORDER * 640.0f);
  y = 0.0f;
  w = PAINBLEND_BORDER * 640.0f;
  h = 480.0f;
  CG_AdjustFrom640(&x, &y, &w, &h);
  s1 = 1.0f - PAINBLEND_BORDER;
  t1 = 0.0f;
  s2 = 1.0f;
  t2 = 1.0f;
  CG_ScalePainBlendTCs(&s1, &t1, &s2, &t2);
  trap_R_DrawStretchPic(x, y, w, h, s1, t1, s2, t2, shader);

  //top
  x = PAINBLEND_BORDER * 640.0f;
  y = 0.0f;
  w = 640.0f - (2 * PAINBLEND_BORDER * 640.0f);
  h = PAINBLEND_BORDER * 480.0f;
  CG_AdjustFrom640(&x, &y, &w, &h);
  s1 = PAINBLEND_BORDER;
  t1 = 0.0f;
  s2 = 1.0f - PAINBLEND_BORDER;
  t2 = PAINBLEND_BORDER;
  CG_ScalePainBlendTCs(&s1, &t1, &s2, &t2);
  trap_R_DrawStretchPic(x, y, w, h, s1, t1, s2, t2, shader);

  //bottom
  x = PAINBLEND_BORDER * 640.0f;
  y = 480.0f - (PAINBLEND_BORDER * 480.0f);
  w = 640.0f - (2 * PAINBLEND_BORDER * 640.0f);
  h = PAINBLEND_BORDER * 480.0f;
  CG_AdjustFrom640(&x, &y, &w, &h);
  s1 = PAINBLEND_BORDER;
  t1 = 1.0f - PAINBLEND_BORDER;
  s2 = 1.0f - PAINBLEND_BORDER;
  t2 = 1.0f;
  CG_ScalePainBlendTCs(&s1, &t1, &s2, &t2);
  trap_R_DrawStretchPic(x, y, w, h, s1, t1, s2, t2, shader);

  trap_R_SetColor(NULL);
}

/*
 =====================
 CG_ResetPainBlend
 =====================
 */
void
CG_ResetPainBlend(void)
{
  cg.painBlendValue = 0.0f;
  cg.painBlendTarget = 0.0f;
  cg.lastHealth = cg.snap->ps.stats[STAT_HEALTH];
}

/*
 =====================
 CG_DrawActive

 Perform all drawing needed to completely fill the screen
 =====================
 */
void
CG_DrawActive(stereoFrame_t stereoView)
{
  float separation;
  vec3_t baseOrg;

  // optionally draw the info screen instead
  if (!cg.snap)
    return;

  switch(stereoView)
  {
    case STEREO_CENTER:
      separation = 0;
      break;
    case STEREO_LEFT:
      separation = -cg_stereoSeparation.value / 2;
      break;
    case STEREO_RIGHT:
      separation = cg_stereoSeparation.value / 2;
      break;
    default:
      separation = 0;
      CG_Error("CG_DrawActive: Undefined stereoView");
  }

  // clear around the rendered view if sized down
  CG_TileClear();

  // offset vieworg appropriately if we're doing stereo separation
  VectorCopy( cg.refdef.vieworg, baseOrg );

  if (separation != 0)
    VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ],
        cg.refdef.vieworg );

  // draw 3D view
  trap_R_RenderScene(&cg.refdef);

  // restore original viewpoint if running stereo
  if (separation != 0)
    VectorCopy( baseOrg, cg.refdef.vieworg );

  // first person blend blobs, done after AnglesToAxis
  /*if( !cg.renderingThirdPerson )*/
  CG_PainBlend();

  // draw status bar and other floating elements
  CG_Draw2D();
}

